interface BaseResponse<T> {
  code: number;
  data: T;
  message: string;
}

interface PageResponse<T> {
  records: T[];
  total: number;
  page: number;
  size: number;
}

const DEFAULT_HEADERS: HeadersInit = {
  'Content-Type': 'application/json',
};

const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? '';

function withBase(path: string) {
  if (path.startsWith('http')) {
    return path;
  }
  return `${'$'}{API_BASE_URL}${'$'}{path}`;
}

function appendQuery(path: string, params?: Record<string, unknown>) {
  if (!params) {
    return path;
  }
  const entries = Object.entries(params).filter(([, value]) => value !== undefined && value !== null && value !== '');
  if (entries.length === 0) {
    return path;
  }
  const query = new URLSearchParams();
  entries.forEach(([key, value]) => {
    if (Array.isArray(value)) {
      value.forEach((item) => query.append(key, String(item)));
    } else {
      query.append(key, String(value));
    }
  });
  const separator = path.includes('?') ? '&' : '?';
  return `${'$'}{path}${'$'}{separator}${'$'}{query.toString()}`;
}

function buildHeaders(headers?: HeadersInit): HeadersInit {
  if (!headers) {
    return DEFAULT_HEADERS;
  }
  const result = new Headers(DEFAULT_HEADERS);
  const incoming = new Headers(headers);
  incoming.forEach((value, key) => result.set(key, value));
  return result;
}

function isBaseResponse<T>(payload: unknown): payload is BaseResponse<T> {
  return (
    typeof payload === 'object' &&
    payload !== null &&
    'code' in payload &&
    'data' in payload
  );
}

async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
  const response = await fetch(withBase(path), {
    ...init,
    headers: buildHeaders(init.headers),
  });

  if (!response.ok) {
    const message = await response.text();
    throw new Error(message || `请求失败（${'$'}{response.status}）`);
  }

  const contentType = response.headers.get('content-type') ?? '';
  if (!contentType.includes('application/json')) {
    return (await response.text()) as unknown as T;
  }

  const payload = (await response.json()) as BaseResponse<T> | T;
  if (isBaseResponse<T>(payload)) {
    if (payload.code !== 200) {
      throw new Error(payload.message || '请求失败');
    }
    return payload.data;
  }

  return payload as T;
}

export const api = {
  get<T>(path: string, params?: Record<string, unknown>) {
    return request<T>(appendQuery(path, params), { method: 'GET' });
  },
  post<T>(path: string, body: unknown, params?: Record<string, unknown>) {
    return request<T>(appendQuery(path, params), {
      method: 'POST',
      body: JSON.stringify(body ?? {}),
    });
  },
  put<T>(path: string, body: unknown, params?: Record<string, unknown>) {
    return request<T>(appendQuery(path, params), {
      method: 'PUT',
      body: JSON.stringify(body ?? {}),
    });
  },
  remove<T>(path: string, params?: Record<string, unknown>) {
    return request<T>(appendQuery(path, params), { method: 'DELETE' });
  },
};

export type { BaseResponse, PageResponse };

